#include "StdAfx.h"

Camera* Singleton<Camera>::m_instance = 0;

Camera::Camera() : m_cam(29.5f * TILESIZE, 200, 40.0f * TILESIZE), m_tar(29.5f * TILESIZE, 200.0f, 40.5f * TILESIZE), m_up(0.0f, 1.0f, 0.0f),
	m_lastCheck(0)
{
	m_mouseLast.x = m_mouseLast.y = -1;
	D3DXVECTOR3 front = m_tar - m_cam;
	D3DXVec3Normalize(&front, &front);
	D3DXVECTOR3 left;
	D3DXVec3Cross(&left, &front, &m_up);
	D3DXVec3Normalize(&left, &left);
	D3DXMATRIX rot;
	D3DXMatrixRotationAxis(&rot, &left, D3DXToRadian(-45.0f));
	D3DXVECTOR4 vOut;
	front = m_tar - m_cam;
	D3DXVec3Transform(&vOut, &front, &rot);
	D3DXVECTOR3 tmp = D3DXVECTOR3(vOut.x, vOut.y, vOut.z);
	m_tar = m_cam + tmp;
	D3DXVec3Transform(&vOut, &m_up, &rot);
	m_up.x = vOut.x;
	m_up.y = vOut.y;
	m_up.z = vOut.z;
	new Loader();

	D3DXCreateSphere(sD3D, 2.0f * TILESIZE, 100, 100, &m_skySphere, 0);
	D3DXMatrixIdentity(&m_matView);
}

void Camera::OnMouseMove(Vertex2F pos)
{
	if(m_mouseLast.x < 0)
	{
		m_mouseLast = pos;
		return;
	}
	float diffX = pos.x - m_mouseLast.x;
	float diffY = pos.y - m_mouseLast.y;
	if(diffY)
	{
		D3DXVECTOR3 front = m_tar - m_cam;
		D3DXVec3Normalize(&front, &front);
		D3DXVECTOR3 left;
		D3DXVec3Cross(&left, &front, &m_up);
		D3DXVec3Normalize(&left, &left);
		D3DXMATRIX rot;
		D3DXMatrixRotationAxis(&rot, &left, -D3DXToRadian(0.09f) * diffY);
		D3DXVECTOR4 vOut;
		front = m_tar - m_cam;
		D3DXVec3Transform(&vOut, &front, &rot);
		D3DXVECTOR3 tmp = D3DXVECTOR3(vOut.x, vOut.y, vOut.z);
		m_tar = m_cam + tmp;
		D3DXVec3Transform(&vOut, &m_up, &rot);
		m_up.x = vOut.x;
		m_up.y = vOut.y;
		m_up.z = vOut.z;
		m_posChanged = true;
	}
	if(diffX)
	{
		D3DXVECTOR3 rotV(0.0f, 1.0f, 0.0f);
		D3DXMATRIX rot;
		D3DXMatrixRotationAxis(&rot, &rotV, -D3DXToRadian(0.09f) * diffX);
		D3DXVECTOR3 front = m_tar - m_cam;
		D3DXVECTOR4 vOut;
		D3DXVec3Transform(&vOut, &front, &rot);
		D3DXVECTOR3 tmp = D3DXVECTOR3(vOut.x, vOut.y, vOut.z);
		m_tar = m_cam + tmp;
		D3DXVec3Transform(&vOut, &m_up, &rot);
		m_up.x = vOut.x;
		m_up.y = vOut.y;
		m_up.z = vOut.z;
		m_posChanged = true;
	}
	m_mouseLast = pos;
}

void Camera::Render()
{
	if(sD3D.GetAppState() == STATE_WORLD) {
		//Loader::GetInstance()->Update(m_cam.x, m_cam.y, m_cam.z);
		sTerrainMgr.pulse();
	}
	if(CheckKeys() || m_posChanged)
	{
		D3DXMATRIX matLook;
		D3DXMatrixLookAtRH(&matLook, &m_cam, &m_tar, &m_up);
		sD3D->SetTransform(D3DTS_VIEW, &matLook);
		m_matView = matLook;
		m_posChanged = false;
	}

	if(sD3D.GetAppState() == STATE_WORLD)
	{
		D3DXMATRIX matWorld;
		D3DXMatrixTranslation(&matWorld, m_cam.x, m_cam.y, m_cam.z);
		sD3D->SetTransform(D3DTS_WORLD, &matWorld);
		m_skySphere->DrawSubset(0);
		sD3D->SetTransform(D3DTS_WORLD, &matID);
	}
}

bool Camera::CheckKeys()
{
	if(InputBox::GetInstance() && sInput.HasFocus())
		return false;
	if(sUI.GetEditFocus())
		return false;

	bool change = false;
	ui32 now = GetTickCount();
	if(m_lastCheck == 0)
	{
		m_lastCheck = now;
		return false;
	}
	BYTE keys[256];
	GetKeyboardState(keys);
	D3DXVECTOR3 dir = m_tar - m_cam;
	D3DXVec3Normalize(&dir, &dir);
	ui32 diff = now - m_lastCheck;
	if(keys[0x57] & 0x80)
	{
		D3DXVECTOR3 tmp = m_cam + (dir / 10) * diff;
		ui32 indexX = (tmp.x / TILESIZE);
		ui32 indexZ = (tmp.z / TILESIZE);
		if(!sD3D.GetADT(indexX, indexZ))
		{
			m_lastCheck = now;
			return false;
		}
		if(tmp.x >= 0 && tmp.z >= 0)
		{
			m_cam += (dir / 10) * diff;
			m_tar += (dir / 10) * diff;
			change = true;
		}
	}
	if(keys[0x53] & 0x80)
	{
		D3DXVECTOR3 tmp = m_cam - (dir / 10) * diff;
		ui32 indexX = (tmp.x / TILESIZE);
		ui32 indexZ = (tmp.z / TILESIZE);
		if(!sD3D.GetADT(indexX, indexZ))
		{
			m_lastCheck = now;
			return false;
		}
		if(tmp.x >= 0 && tmp.z >= 0)
		{
			m_cam -= (dir / 10) * diff;
			m_tar -= (dir / 10) * diff;
			change = true;
		}
	}
	if(keys[0x41] & 0x80)
	{
		D3DXVECTOR3 left;
		D3DXVec3Cross(&left, &dir, &m_up);
		D3DXVECTOR3 tmp = m_cam - (left / 10) * diff;
		ui32 indexX = (tmp.x / TILESIZE);
		ui32 indexZ = (tmp.z / TILESIZE);
		if(!sD3D.GetADT(indexX, indexZ))
		{
			m_lastCheck = now;
			return false;
		}
		if(tmp.x >= 0 && tmp.z >= 0)
		{
			m_cam -= (left / 10) * diff;
			m_tar -= (left / 10) * diff;
			change = true;
		}
	}
	if(keys[0x44] & 0x80)
	{
		D3DXVECTOR3 left;
		D3DXVec3Cross(&left, &dir, &m_up);
		D3DXVECTOR3 tmp = m_cam + (left / 10) * diff;
		ui32 indexX = (tmp.x / TILESIZE);
		ui32 indexZ = (tmp.z / TILESIZE);
		if(!sD3D.GetADT(indexX, indexZ))
		{
			m_lastCheck = now;
			return false;
		}
		if(tmp.x >= 0 && tmp.z >= 0)
		{
			m_cam += (left / 10) * diff;
			m_tar += (left / 10) * diff;
			change = true;
		}
	}
	if(keys[0x20] & 0x80)
	{
		m_cam.y += diff / 10.0f;
		m_tar.y += diff / 10.0f;
		change = true;
	}
	if(keys[0x58] & 0x80)
	{
		m_cam.y -=  diff / 10.0f;
		m_tar.y -=  diff / 10.0f;
		change = true;
	}
	if(keys[0xBC] & 0x80)
	{
		D3DXVECTOR3 front = m_tar - m_cam;
		D3DXVec3Normalize(&front, &front);
		D3DXVECTOR3 left;
		D3DXVec3Cross(&left, &front, &m_up);
		D3DXVec3Normalize(&left, &left);
		D3DXMATRIX rot;
		D3DXMatrixRotationAxis(&rot, &left, D3DXToRadian(0.1f) * ((FLOAT)now - m_lastCheck));
		D3DXVECTOR4 vOut;
		front = m_tar - m_cam;
		D3DXVec3Transform(&vOut, &front, &rot);
		D3DXVECTOR3 tmp = D3DXVECTOR3(vOut.x, vOut.y, vOut.z);
		m_tar = m_cam + tmp;
		D3DXVec3Transform(&vOut, &m_up, &rot);
		m_up.x = vOut.x;
		m_up.y = vOut.y;
		m_up.z = vOut.z;
		change = true;
	}
	if(keys[0xBE] & 0x80)
	{
		D3DXVECTOR3 front = m_tar - m_cam;
		D3DXVec3Normalize(&front, &front);
		D3DXVECTOR3 left;
		D3DXVec3Cross(&left, &front, &m_up);
		D3DXVec3Normalize(&left, &left);
		D3DXMATRIX rot;
		D3DXMatrixRotationAxis(&rot, &left, D3DXToRadian(-0.1f) * ((FLOAT)now - m_lastCheck));
		D3DXVECTOR4 vOut;
		front = m_tar - m_cam;
		D3DXVec3Transform(&vOut, &front, &rot);
		D3DXVECTOR3 tmp = D3DXVECTOR3(vOut.x, vOut.y, vOut.z);
		m_tar = m_cam + tmp;
		D3DXVec3Transform(&vOut, &m_up, &rot);
		m_up.x = vOut.x;
		m_up.y = vOut.y;
		m_up.z = vOut.z;
		change = true;
	}
	if(keys[0x51] & 0x80)
	{
		D3DXVECTOR3 rotV(0.0f, 1.0f, 0.0f);
		D3DXMATRIX rot;
		D3DXMatrixRotationAxis(&rot, &rotV, D3DXToRadian(0.1f) * ((FLOAT)now - m_lastCheck));
		D3DXVECTOR3 front = m_tar - m_cam;
		D3DXVECTOR4 vOut;
		D3DXVec3Transform(&vOut, &front, &rot);
		D3DXVECTOR3 tmp = D3DXVECTOR3(vOut.x, vOut.y, vOut.z);
		m_tar = m_cam + tmp;
		D3DXVec3Transform(&vOut, &m_up, &rot);
		m_up.x = vOut.x;
		m_up.y = vOut.y;
		m_up.z = vOut.z;
		change = true;
	}
	if(keys[0x45] & 0x80)
	{
		D3DXVECTOR3 rotV(0.0f, 1.0f, 0.0f);
		D3DXMATRIX rot;
		D3DXMatrixRotationAxis(&rot, &rotV, D3DXToRadian(-0.1f) * ((FLOAT)now - m_lastCheck));
		D3DXVECTOR3 front = m_tar - m_cam;
		D3DXVECTOR4 vOut;
		D3DXVec3Transform(&vOut, &front, &rot);
		D3DXVECTOR3 tmp = D3DXVECTOR3(vOut.x, vOut.y, vOut.z);
		m_tar = m_cam + tmp;
		D3DXVec3Transform(&vOut, &m_up, &rot);
		m_up.x = vOut.x;
		m_up.y = vOut.y;
		m_up.z = vOut.z;
		change = true;
	}
	m_lastCheck = now;
	return change;
}

void Camera::SetCameraPosition(float x, float y, float z)
{
	m_cam.x = x;
	m_cam.y = y;
	m_cam.z = z;
	m_tar.x = x;
	m_tar.y = y;
	m_tar.z = z + 10.0f;
	m_up.x = 0.0f;
	m_up.y = 1.0f;
	m_up.z = 0.0f;
	D3DXVECTOR3 front = m_tar - m_cam;
	D3DXVec3Normalize(&front, &front);
	D3DXVECTOR3 left;
	D3DXVec3Cross(&left, &front, &m_up);
	D3DXVec3Normalize(&left, &left);
	D3DXMATRIX rot;
	D3DXMatrixRotationAxis(&rot, &left, D3DXToRadian(-45.0f));
	D3DXVECTOR4 vOut;
	front = m_tar - m_cam;
	D3DXVec3Transform(&vOut, &front, &rot);
	D3DXVECTOR3 tmp = D3DXVECTOR3(vOut.x, vOut.y, vOut.z);
	m_tar = m_cam + tmp;
	D3DXVec3Transform(&vOut, &m_up, &rot);
	m_up.x = vOut.x;
	m_up.y = vOut.y;
	m_up.z = vOut.z;
	m_posChanged = true;
}

void Camera::OnMouseWheel(i16 delta)
{
	if(sD3D.GetAppState() != STATE_WORLD)
		return;
	D3DXVECTOR3 front = m_tar - m_cam;
	D3DXVec3Normalize(&front, &front);
	if(delta < 0)
	{
		m_cam -= front * 6;
		m_tar -= front * 6;
	}
	else if(delta > 0)
	{
		m_cam += front * 6;
		m_tar += front * 6;
	}
	m_posChanged = true;
}